package multithread.framework.threadmethod;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import multithread.framework.annotation.NoneThreadGroup;
import multithread.framework.annotation.ThreadAfter;
import multithread.framework.annotation.ThreadAllAfter;
import multithread.framework.annotation.ThreadAllBefore;
import multithread.framework.annotation.ThreadBefore;
import multithread.framework.annotation.ThreadGroupAfter;
import multithread.framework.annotation.ThreadGroupBefore;
import multithread.framework.annotation.ThreadGroupFor;
import multithread.framework.utility.Utilities;

/**
 * 
 * a cache for one test case
 */
public class ThreadMethodContainer implements IContainer {
	
	
	private static class InnerSingleton {
		private static final ThreadMethodContainer instance = new ThreadMethodContainer();
	}

	public static ThreadMethodContainer getInstance() {
		return InnerSingleton.instance;
	}

	public static ThreadMethodContainer newInsatnce() {
		return new ThreadMethodContainer();
	}

	public static ThreadMethodContainer newInsatnce(IThreadMethodChecker checker) {
		return new ThreadMethodContainer(checker);
	}

	/**
	 * Map:
	 * 
	 * <pre>
	 * [ThreadAllBefore.class	List<ThreadMethod>] 
	 * [ThreadAllAfter.class	List<ThreadMethod>]
	 * [ThreadBefore.class		List<ThreadMethod>]
	 * [ThreadAfter.class 		List<ThreadMethod>]
	 * [ThreadGroupBefore.class List<ThreadMethod>]
	 * [ThreadGroupAfter.class 	List<ThreadMethod>]
	 * [ThreadGroupFor.class	List<ThreadMethodGroup>] 
	 * [NoneThreadGroup.class 	List<ThreadMethod>]
	 * </pre>
	 */
	private Map<Class<? extends Annotation>, List<IThreadMethod>> container = null;
	private Class<?> testCaseClass = null;
	/**
	 * timeout for method run in multi thread 
	 */
	private long threadMethodTimeout;
	private IThreadMethodChecker checker = null;
	
	private ThreadMethodContainer() {
		container = new HashMap<Class<? extends Annotation>, List<IThreadMethod>>();
	}

	private ThreadMethodContainer(IThreadMethodChecker checker) {
		this();
		this.checker = checker;
	}

	protected void setTestCaseClass(Class<?> testCaseClz) {
		this.testCaseClass = testCaseClz;
	}

	public Class<?> getTestCaseClass() {
		return testCaseClass;
	}

	protected void put(Class<? extends Annotation> annotation,
			List<IThreadMethod> list) {
		container.put(annotation, list);
	}

	protected void reset() {
		container.clear();
	}

	/**
	 * Get ThreadMethod list by annotation type
	 * 
	 * @param annotationTypeClz
	 *            need to get method annotation type
	 * @return List<IThreadMethod>
	 */
	public List<IThreadMethod> getThreadMethods(
			Class<? extends Annotation> annotationTypeClz) {
		List<IThreadMethod> threadMethodList = container.get(annotationTypeClz);
		return Utilities.requireNotNull(threadMethodList);
	}

	/**
	 * Get ThreadMethodGroup list
	 * 
	 * @return List<ThreadMethodGroup>
	 */
	public List<ThreadMethodGroup> getThreadGroups() {
		List<IThreadMethod> threadMethods = Utilities.requireNotNull(container
				.get(ThreadGroupFor.class));
		List<ThreadMethodGroup> threadGroups = new ArrayList<ThreadMethodGroup>();
		for (IThreadMethod method : threadMethods) {
			threadGroups.add((ThreadMethodGroup) method);
		}
		return threadGroups;

	}


	/**
	 * add valid ThreadMethod to cache
	 * 
	 * @param threadMethod
	 */
	public void addThreadMethod(ThreadMethod threadMethod) {
		if (threadMethod == null)
			return;
		if (threadMethod.getThreadAllBefore() != null) {
			regiester(ThreadAllBefore.class, threadMethod);
		}
		if (threadMethod.getThreadAllAfter() != null) {
			regiester(ThreadAllAfter.class, threadMethod);
		}
		if (threadMethod.getThreadGroupBefore() != null) {
			regiester(ThreadGroupBefore.class, threadMethod);
		}
		if (threadMethod.getThreadGroupAfter() != null) {
			regiester(ThreadGroupAfter.class, threadMethod);
		}
		if (threadMethod.getThreadBefor() != null) {
			regiester(ThreadBefore.class, threadMethod);
		}
		if (threadMethod.getThreadAfter() != null) {
			regiester(ThreadAfter.class, threadMethod);
		}
		if (threadMethod.getThreadGroupFor() == null
				&& threadMethod.getThreadFor() != null) {
			if (threadMethod.getThreadFor().value() > 0) {// check invalid
				regiester(NoneThreadGroup.class, threadMethod);
			}
		}
		if (threadMethod.getThreadGroupFor() != null
				&& threadMethod.getThreadFor() != null) {
			if (threadMethod.getThreadFor().value() > 0) {// check invalid
				addThreadGroup(threadMethod);
			}
		}

	}

	/**
	 * add thread method to group structure like following:
	 * 
	 * <pre>
	 * ThreadGroupFor.class ThreadMethodGroup-A ThreadMethod-A1 ThreadMethod-A2
	 * 						ThreadMethodGroup-B	ThreadMethod-B1 ThreadMethod-B2
	 * 		     			ThreadMethodGroup-C ThreadMethod-C1
	 * </pre>
	 * 
	 * @param threadMethod
	 */
	private void addThreadGroup(ThreadMethod threadMethod) {
		List<IThreadMethod> threadMethods = container.get(ThreadGroupFor.class);
		ThreadMethodGroup group = null;
		if (threadMethods == null) {
			threadMethods = new ArrayList<IThreadMethod>();
		}else{
			if(checker!=null){
				checker.checkDuplicateGroup(threadMethods, threadMethod);
			}	
		}
		ThreadMethodGroup grouptemp = new ThreadMethodGroup(
				threadMethod.getThreadGroupFor());
		if (threadMethods.indexOf(grouptemp) < 0) {
			group = grouptemp;
			threadMethods.add(group);
		}
		int groupIndex = threadMethods.indexOf(grouptemp);
		group = (ThreadMethodGroup) threadMethods.get(groupIndex);
		group.addThreadMethod(threadMethod);
		container.put(ThreadGroupFor.class, threadMethods);
	}

	private void regiester(Class<? extends Annotation> annotationType,
			ThreadMethod threadMethod) {
		List<IThreadMethod> threadMethods = container.get(annotationType);
		if (threadMethods == null) {
			threadMethods = new ArrayList<IThreadMethod>();
		}
		threadMethods.add(threadMethod);
		container.put(annotationType, threadMethods);
	}

	@Override
	public String toString() {
		return "ThreadMethodContainer [testCaseClass=" + testCaseClass + "]";
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ThreadMethodContainer other = (ThreadMethodContainer) obj;
		if (testCaseClass == null) {
			if (other.testCaseClass != null)
				return false;
		} else if (testCaseClass != other.testCaseClass)
			return false;
		return true;
	}

	public long getThreadMethodTimeout() {
		return threadMethodTimeout;
	}

	
	public void setThreadMethodTimeout(long threadMethodTimeout) {
		this.threadMethodTimeout = threadMethodTimeout;
	}
}
